//------------------------------------------------------------------------------
//SFWK Framework
//Copyright (C) 2011 SQLI

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Reflection;
using SFWK.Core.DTO;
using System.Linq.Expressions;
using System.Collections;

namespace SFWK.Core.Linq
{


    [DataContract]
    [Serializable]
    public abstract class ControlledGraph
    {
    }

    [DataContract]
    [Serializable]
    public class ControlledGraph<TEntityRoot> : ControlledGraph
    {
        private List<string> _allreadyCheckedType;

        [DataMember]
        public List<string> Graph
        {
            get
            {
                return _graphs;
            }
            set
            {
                _graphs = value;
            }
        }
        private List<string> _graphs;

        public ControlledGraph()
        {
            _graphs = new List<string>();
        }
        /*
        #region Properties
        [DataMember]
        public List<string> ChildEntities
        {
            get { return _childEntities; }
        }
        private List<string> _childEntities;
        #endregion Properties

        #region Constructor
        public ControlledGraph()
        {
            _childEntities = new List<string>();
        }

        public ControlledGraph(ControlledGraph<TEntityRoot> graph)
            : this()
        {
            this._childEntities.AddRange(graph.ChildEntities);
        }
        #endregion Constructor

        #region Public methods
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<String> GetGraphList()
        {
            //string res = "";
            //foreach (string graph in _childEntities)
            //    res += graph + ".";
            //return res.Contains(".") ? res.TrimEnd('.') : null;
            return new List<string>();
        }

        public string GetGraph()
        {
            string res = "";
            foreach (string graph in _childEntities)
                res += graph + ".";
            return res.Contains(".") ? res.TrimEnd('.') : null;
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TChildEntity"></typeparam>
        /// <returns></returns>
        public virtual ControlledGraph<TEntityRoot> Add<TChildEntity>()
        {
            string childEntyTypeName = (typeof(TChildEntity)).Name;

            if (_childEntities.Count == 0) // Root object case
            {
                foreach (PropertyInfo property in (typeof(TEntityRoot)).GetProperties())
                {
                    if (property.PropertyType == typeof(List<TChildEntity>) || property.PropertyType == typeof(TrackableCollection<TChildEntity>) || (property.PropertyType == typeof(TChildEntity)))
                    {
                        if (!_childEntities.Contains(property.Name))
                            _childEntities.Add(property.Name);
                        break;
                    }
                }
            }
            else //Subobject case -> Recurse childs...
            {
                Type parentType = typeof(TEntityRoot);
                foreach (string relationName in _childEntities)
                {
                    foreach (PropertyInfo property in parentType.GetProperties())
                    {
                        if (property.PropertyType.Name == relationName)
                        {
                            parentType = property.PropertyType;
                            break;
                        }
                    }
                }
                foreach (PropertyInfo property in parentType.GetProperties())
                {
                    if (property.PropertyType == typeof(List<TChildEntity>) || property.PropertyType == typeof(TrackableCollection<TChildEntity>) || (property.PropertyType == typeof(TChildEntity)))
                    {
                        if (!_childEntities.Contains(property.Name))
                            _childEntities.Add(property.Name);
                        break;
                    }
                }
            }
            return this;
        }


        public virtual ControlledGraph<TEntityRoot> Add<TChildEntity>(ControlledGraph childGraph)
        {
            return this;
        }
         
        #endregion Public methods
        

        //public ControlledGraph<TEntityRoot> Add(Expression<Func<TEntityRoot, object>> expression)
        //{
        //    if (expression != null)
        //    {
        //        string graph = "";
        //        graph = ((MemberExpression)expression.Body).Member.Name;
        //        if (!_graphs.Contains(graph))
        //            _graphs.Add(graph);
        //    }
        //    return this;
        //}

        //public ControlledGraph<TEntityRoot> Add<TEntity>(Expression<Func<TEntity, object>> expression)
        //{
        //    if (expression != null)
        //    {
        //        string graphChild = "";
        //        graphChild = ((MemberExpression)expression.Body).Member.Name;
        //        string parent = typeof(TEntity).Name;
        //        for(int i=0; i<this.Graph.Count;i++)
        //        {
        //            if (this.Graph[i].Contains(parent))
        //            {
        //                this.Graph[i] = this.Graph[i] + "." + graphChild;
        //                break;
        //            }
        //        }
        //    }
        //    return this;
        //}

        */



        public ControlledGraph<TEntityRoot> Add<TChildEntity>()
        {
            //string childEntyTypeName = (typeof(TChildEntity)).Name;
            Type parentType = typeof(TEntityRoot);
            string foundGraph = "";
            _allreadyCheckedType = new List<string>();
            if (_recurseTypesToBuildGraph<TChildEntity>(parentType, ref foundGraph))
            {
                if (!string.IsNullOrEmpty(foundGraph))
                {
                    foundGraph = foundGraph.TrimEnd('.');
                    if (!_graphs.Contains(foundGraph))
                        _graphs.Add(foundGraph);
                }
            }
            _allreadyCheckedType = new List<string>();
            return this;
        }

        public ControlledGraph<TEntityRoot> Add<TEntity>(ControlledGraph<TEntity> graph)
        {
            // - 13.09.2010 - Refactor the method because was a bug when entityname != linkName
            List<string> previousGraph = new List<string>(_graphs);

            Add<TEntity>();

            string addedGraph = _graphs.Where(s => !previousGraph.Contains(s)).FirstOrDefault();

            if (graph != null && graph.Graph != null && graph.Graph.Count > 0)
            {
                foreach (string g in graph.Graph)
                {
                    string newGraph = addedGraph + "." + g;

                    if (!this.Graph.Contains(newGraph))
                    {
                        List<string> graphs = this.Graph.ToList();
                        foreach (var itGraph in graphs)
                        {
                            if (newGraph.Contains(itGraph))
                            {
                                this.Graph.Remove(itGraph);
                            }
                        }
                        this.Graph.Add(newGraph);
                    }
                }
            }
            return this;
            //NAE
        }


        private bool _recurseTypesToBuildGraph<TChildEntity>(Type parent, ref string foundGraph)
        {
            _allreadyCheckedType.Add(parent.Name);
            // - 22.07.2010 - Add an OrderBy call to priorize the property containing type TChildEntity
            var props = parent.GetProperties().Where(p =>
                ((p.PropertyType.GetInterfaces().Where(interf => interf.Name.Equals("IObjectWithChangeTracker")).Count() > 0) || p.PropertyType.Name.StartsWith("TrackableCollection")) &&
                (p.PropertyType != typeof(TEntityRoot)) &&
                !p.PropertyType.Name.StartsWith("ControlledGraph")
                ).ToList();

            props.Sort(new EntityComparer<TChildEntity>());

            foreach (PropertyInfo property in props)
            //
            {
                if (property.PropertyType == typeof(List<TChildEntity>) || property.PropertyType == typeof(TrackableCollection<TChildEntity>) || (property.PropertyType == typeof(TChildEntity)))
                {
                    foundGraph += property.Name + ".";
                    return true;
                }
                else
                {
                    Type newParent = property.PropertyType;
                    if (property.PropertyType.Name.StartsWith("TrackableCollection"))
                        newParent = property.PropertyType.GetGenericArguments()[0];
                    if (newParent != typeof(TEntityRoot))
                    {
                        if (!_allreadyCheckedType.Contains(newParent.Name))
                        {
                            if (_recurseTypesToBuildGraph<TChildEntity>(newParent, ref foundGraph))
                            {
                                foundGraph = property.Name + "." + foundGraph;
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }


        /// <summary>
        /// An intern class for sorting all propertyInfo to priorize properties containing type TEntity
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        private class EntityComparer<TEntity> : IComparer<PropertyInfo>
        {
            public int Compare(PropertyInfo x, PropertyInfo y)
            {
                Type xType = x.PropertyType;
                Type yType = y.PropertyType;

                int result = 1;

                if (xType == yType)
                {
                    return 0;
                }
                else if (xType == typeof(List<TEntity>) || xType == typeof(TrackableCollection<TEntity>) || xType == typeof(TEntity))
                {
                    result = -1;
                }
                else if (yType == typeof(List<TEntity>) || yType == typeof(TrackableCollection<TEntity>) || yType == typeof(TEntity))
                {
                    result = 1;
                }
                else
                {
                    result = x.PropertyType.Name.CompareTo(y.PropertyType.Name);
                }
                return result;
            }
        }
    }

}

